/*
 * 	kernel/ipc.c
 *
 * 	created at 2007.10.02 by ChenJie
 *
 * 	last modified at 2007.10.02
 *
 */
 
#include <cos/ipc.h>
#include <cos/sched.h>
#include <cos/protect.h>

long receive_notify(long src_pid, struct msg_struct * user_msg_ptr);

static void msg_init(struct msg_struct *p)
{
	int i;
	p->source = 0;
	p->type = 0;
	p->time = 0;
	for(i=0; i<NR_MSG_CONTEXT; i++) p->context[i]=0;	
}
void msgbox_init(struct msgbox_struct *p)
{
	int i;
	msg_init(&p->sendbox);
	msg_init(&p->receivebox);
	p->notify_pending = 0;
	for(i=0; i<NR_NOTIFY_CONTEXT; i++) p->notify_context[i]=0;
}

long sys_send(long dst_pid, struct msg_struct * user_msg_ptr, unsigned long type)
{
	struct proc_struct * dst_proc = get_proc_ptr(dst_pid);
	if(dst_proc==NULL) return FAILURE;
	 if(is_sleeping_on(dst_pid, 
	 	&(procs.running_proc->waiting_for_sending))==TRUE){
	 	copy_user_to_kernel(user_msg_ptr, 
			&(dst_proc->msgbox.receivebox), sizeof(struct msg_struct));
		dst_proc->msgbox.receivebox.type = type;
		wake_up(dst_pid, &(procs.running_proc->waiting_for_sending));
	 }else if(is_sleeping_on(dst_pid, 
	 	&(dst_proc->waiting_for_any_arrive))==TRUE){
	 	copy_user_to_kernel(user_msg_ptr, 
			&(dst_proc->msgbox.receivebox), sizeof(struct msg_struct));
		dst_proc->msgbox.receivebox.type = type;
		wake_up(dst_pid, &(dst_proc->waiting_for_any_arrive));
	 }else{
		copy_user_to_kernel(user_msg_ptr, 
			&(procs.running_proc->msgbox.sendbox), sizeof(struct msg_struct)); 
		procs.running_proc->msgbox.sendbox.type = type;
		sleep_on(&(dst_proc->waiting_for_receiving));
	 }
	 return SUCCESS;
}




long sys_receive(long src_pid, struct msg_struct * user_msg_ptr)
{
	 struct proc_struct * src_proc=NULL;
	 if(procs.running_proc->msgbox.notify_pending 
	 	&& (receive_notify(src_pid, user_msg_ptr)==SUCCESS)){
	 	return SUCCESS;
	 }
	 if(is_sleeping_on(src_pid, &(procs.running_proc->waiting_for_receiving))==TRUE){
	 	if(src_pid == ANY_PROC){
			src_pid = procs.running_proc->waiting_for_receiving->pid;
	 	}
		src_proc = get_proc_ptr(src_pid);
		if(src_proc == NULL) return FAILURE;
		copy_kernel_to_user(&(src_proc->msgbox.sendbox), 
			user_msg_ptr, sizeof(struct msg_struct));
		wake_up(src_pid, &(procs.running_proc->waiting_for_receiving));
	 }else{
		 if(src_pid==ANY_PROC) {
		 	sleep_on(&(procs.running_proc->waiting_for_any_arrive));
		 }else{
			 src_proc = get_proc_ptr(src_pid);
			 if(src_proc == NULL) return FAILURE;
			sleep_on(&(src_proc->waiting_for_sending));
		 }
		 copy_kernel_to_user(&(procs.running_proc->msgbox.receivebox), 
		 	user_msg_ptr, sizeof(struct msg_struct));
	 }
	
	 return SUCCESS;
}

long sys_notify(long src_pid, long dst_pid)
{
	struct proc_struct *dst_proc = get_proc_ptr(dst_pid);
	struct proc_struct *src_proc = get_proc_ptr(src_pid);
	struct msg_struct * msgp = &(dst_proc->msgbox.receivebox);
	unsigned long notification = 0;
	unsigned long type = 0;
	
	if(dst_proc==NULL) return FAILURE;
	
	switch(src_pid){
	case KERNEL:
		notification = (1<<NOTIFIER_BIT(src_pid));
		type = SOFTWARE_INT;
		break;
	case CLOCK:
		notification = (1<<NOTIFIER_BIT(src_pid));
		type = CLOCK_NOTIFICATION;
		break;
	case SYSTEM:
		notification = (1<<NOTIFIER_BIT(src_pid));
		type = SYSTEM_NOTIFICATION;
		break;
	case HARDWARE:
		notification = (1<<NOTIFIER_BIT(src_pid));
		type = HARDWARE_INT;
		break;
	default:
		return FAILURE;
	}
	if(is_sleeping_on(dst_pid, 
		&(src_proc->waiting_for_sending))==TRUE){
	 	msgp->context[0] = notification;
		msgp->source = src_pid;
		msgp->time = procs.jiffies;
		msgp->type = type;
		wake_up(dst_pid, &(src_proc->waiting_for_sending));
	 }else if(is_sleeping_on(dst_pid, 
	 	&(dst_proc->waiting_for_any_arrive))==TRUE){
	 	msgp->context[0] = notification;
		msgp->source = src_pid;
		msgp->time = procs.jiffies;
		msgp->type = type;
		wake_up(dst_pid, &(dst_proc->waiting_for_any_arrive));
	 }else{
		dst_proc->msgbox.notify_pending |= (1<<NOTIFIER_BIT(src_pid));
		dst_proc->msgbox.notify_context[NOTIFIER_BIT(src_pid)] = notification;
	 }
	 return SUCCESS;
}

long receive_notify(long src_pid, struct msg_struct * user_msg_ptr)
{
	 struct msg_struct msg;
	 int i;
	 if(src_pid == ANY_PROC){
	 	for(i=0;i<NR_NOTIFY_CONTEXT; i++){
		 	if( (1<<i)&(procs.running_proc->msgbox.notify_pending)) break;
	 	}
		if(i==NR_NOTIFY_CONTEXT) return FAILURE;
		src_pid = NOTIFIER_PID(i);
	 }
	 switch(src_pid){
	 case KERNEL:
	 	msg.source = KERNEL;
		msg.type = SOFTWARE_INT;
		msg.time = procs.jiffies;
		msg.context[0] = procs.running_proc->msgbox.notify_context[NOTIFIER_BIT(KERNEL)];
		break;
	 case CLOCK:
	 	msg.source = CLOCK;
		msg.type = CLOCK_NOTIFICATION;
		msg.time = procs.jiffies;
		msg.context[0] = procs.running_proc->msgbox.notify_context[NOTIFIER_BIT(CLOCK)];
	 	break;
	 case SYSTEM:
	 	msg.source = SYSTEM;
		msg.type = SYSTEM_NOTIFICATION;
		msg.time = procs.jiffies;
		msg.context[0] = procs.running_proc->msgbox.notify_context[NOTIFIER_BIT(SYSTEM)];
	 	break;
	 case HARDWARE:
	 	msg.source = HARDWARE;
		msg.type = HARDWARE_INT;
		msg.time = procs.jiffies;
		msg.context[0] = procs.running_proc->msgbox.notify_context[NOTIFIER_BIT(HARDWARE)];
	 	break;
	default:
		return FAILURE;
	 }
	 copy_kernel_to_user(&msg, 
		user_msg_ptr, sizeof(struct msg_struct));
	 
	 //clear the pending
	 procs.running_proc->msgbox.notify_pending &= (~(1<<NOTIFIER_BIT(src_pid)));
	 return SUCCESS;
}

long sys_sendrec(long dst_pid, struct msg_struct * user_msg_ptr)
{
	if(sys_send(dst_pid, user_msg_ptr, SENDREC)==FAILURE) return FAILURE;
	if(sys_receive(dst_pid, user_msg_ptr)==FAILURE) return FAILURE;
	return SUCCESS;
}

